/// <reference path="../disco/disco.ts" />
/// <reference path="../typings/knockout/knockout.d.ts" />
/// <reference path="../typings/jquery/jquery.d.ts" />

module disco.viewmodel.home {
    export class Index {

        public init(): void {
            var viewmodel = new disco.viewmodel.LibraryDemoView();
            var ontology = viewmodel.ontology;

            var testFty = new TestFactory(viewmodel);

            var tests = viewmodel.tests;
            var eContent = null, ePost = null; //entities
            var tagId = 3;
            tests.push(new disco.tests.Test('init()',
                ready => {
                    ontology.init(
                        context => {
                            ready();
                        },
                        error => {
                            ready('FAIL', error, true);
                        });
                }));

            tests.push(testFty.AddEntity('addEntity(): Content <c1>',
                () => ({
                    type: ontology.dataContext.entityType('Content'),
                    properties: { Title: "Title", Text: "Text", CultureId: 2 }
                }),
                d => { eContent = d; return false }, e => false));

            tests.push(testFty.AddEntity('addEntity(): Post <p1>',
                () => ({
                    type: ontology.dataContext.entityType('Post'),
                    properties: { ContentId: eContent.prop('Id').value, PostTypeId: 1, TopicId: 1 }
                }),
                d => { ePost = d; return false }, e => true));

            var tPostFn = () => ontology.dataContext.entityType('Post');
            var pPostTagsFn = () => Collections.single(tPostFn().properties, p => p.name == "Tags");

            tests.push(testFty.AddConnection('addConnection(): <c1> = <p1>/Tags => Tags(' + tagId + ')',
                tPostFn, () => ePost.prop('Id').value, pPostTagsFn, () => tagId));

            tests.push(new disco.tests.Test('getEntitiesByPath(): <p1>/Tags',
                ready => {
                    ontology.getEntitiesByPath('Posts(' + ePost.prop('Id').value + ')/Tags',
                        entities => {
                            ready('OK', JSON.stringify(entities.map(x => 'Tags(' + x.prop('Id').value + ')')));
                        },
                        error => {
                            ready('FAIL', error);
                        });
                }));

            tests.push(testFty.RemoveConnection('removeConnection(): Connection <c1>',
                tPostFn, () => ePost.prop('Id').value, pPostTagsFn, () => tagId));

            tests.push(testFty.RemoveEntity('removeEntity(): <p1>',
                () => ontology.dataContext.entityType('Post'), () => ePost.prop('Id').value));

            tests.push(testFty.RemoveEntity('removeEntity(): <c1>',
                () => ontology.dataContext.entityType('Content'), () => eContent.prop('Id').value));

            var t = new disco.tests.Test('Callbacks.allAtOnce()',
                ready => {
                    Callbacks.allAtOnce(
                        [
                            ready => { setTimeout(() => { t.result('100'); ready() }, 100) },
                            ready => { setTimeout(() => { t.result('200'); ready() }, 200) }
                        ],
                        () => { ready('OK', '') });
                });
            tests.push(t);

            var t2 = new disco.tests.Test('Ontology.getEntitiesByTemplate()',
                ready => {
                    ontology.getEntitiesByTemplate(
                        {
                            type: ontology.dataContext.entityType('Post'),
                            properties: {
                                Id: 1,
                                ReferredFrom: {
                                    properties: {
                                        Id: 2,
                                        Referrer: { properties: { Content: { properties: { Text: "Ist direkte Demokratie machbar?" } } } },
                                        Referree: {}
                                    }
                                }
                            }
                        },
                        (entities, x) => {
                            console.log('really ready');
                            var e = entities[0];
                            ready('OK', JSON.stringify(e.prop('ReferredFrom').value[0].prop('Referrer').prop('Content'), disco.model.stringifyReplacer));
                        }
                        );
                });
            tests.push(t2);

            Callbacks.batch(tests().map(t => t.test.bind(t)), () => { });

            ko.applyBindings(viewmodel);
        }
    }

    class TestFactory {
        private vmodel: viewmodel.LibraryDemoView;
        private ontology: Ontology;

        constructor(viewmodel: viewmodel.LibraryDemoView) {
            this.vmodel = viewmodel;
            this.ontology = this.vmodel.ontology;
        }

        public AddEntity(title: string, entityFn: () => any, onSuccess: (data) => boolean = () => false, onError: (error) => boolean = () => false): tests.Test {
            return new tests.Test(title, ready => {
                this.ontology.addEntity(
                    entityFn(),
                    d => { ready('OK', JSON.stringify(d, model.stringifyReplacer), onSuccess(d)) },
                    error => { ready('FAIL', error, onError(error)) });
            });
        }

        public RemoveEntity(title: string, typeFn: () => model.EntityType, idFn: () => any, onSuccess: () => boolean = () => false, onError: (error) => boolean = () => false): tests.Test {
            return new tests.Test(title, ready => {
                this.ontology.removeEntity(typeFn(), idFn(),
                    () => { ready('OK', '', onSuccess()); },
                    e => {
                        if (e == 'Not Found')
                            ready('WARNING', e, onError(e));
                        else
                            ready('FAIL', e, onError(e));
                    });
            });
        }

        public AddConnection(title: string, entityTypeFn: () => model.EntityType, idFn: () => any, propertyFn: () => model.EntityProperty, valueIdFn: () => any, onSuccess: () => boolean = () => false, onError: (error) => boolean = () => false): tests.Test {
            return new tests.Test(title, ready => {
                this.ontology.addConnection(
                    entityTypeFn(), idFn(), propertyFn(), valueIdFn(),
                    () => { ready('OK', '', onSuccess()); },
                    error => { ready('FAIL', error, onError(error)); });
            });
        }

        public RemoveConnection(title: string, entityTypeFn: () => model.EntityType, idFn: () => any, propertyFn: () => model.EntityProperty, valueIdFn: () => any, onSuccess: () => boolean = () => false, onError: (error) => boolean = () => false): tests.Test {
            return new tests.Test(title, ready => {
                this.ontology.removeConnection(
                    entityTypeFn(), idFn(), propertyFn(), valueIdFn(),
                    () => { ready('OK', '', onSuccess()); },
                    error => { ready('FAIL', error, onError(error)); });
            });
        }
    }
}

module disco.tests {
    export class Test {
        public result = ko.observable<string>('');
        public state = ko.observable<string>('');
        public title = ko.observable<string>('');

        public test(ready: () => void = null) {
            try {
                this.testMethod((state = 'OK', result = '', cancel = false) => { this.result(result); this.state(state); if (!cancel) ready() });
            }
            catch (e) {
                this.result(e.toString());
                this.state('ERROR');
                ready();
            }
        }

        private testMethod: (ready: (state?: string, result?: string, cancel?: boolean) => void) => void;

        constructor(title: string, testMethod: (ready: (state?: string, result?: string, cancel?: boolean) => void) => void) {
            this.title(title);
            this.testMethod = testMethod;
        }
    }
}

module disco.viewmodel {
    export class LibraryDemoView {
        public isBusy = false;
        public ontology = new Ontology('http://localhost:50256/api/odata/');

        public tests = ko.observableArray<tests.Test>([]);
    }
}

jQuery(document).ready(() => {
    var view = new disco.viewmodel.home.Index();
    view.init();
});